package com.liliudong.template.api.admin.handler;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.liliudong.template.common.security.util.SecurityUtil;
import com.liliudong.template.common.annotation.DataScope;
import com.liliudong.template.common.constant.SysRoleDataScopeEnum;
import com.liliudong.template.common.util.MappedStatementUtils;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author ldli5
 * @classname SimpleDataPermissionHanlder
 * @date 2024/6/19-9:13
 */
@Component
public class SimpleDataPermissionHandler implements DataPermissionHandler {

	private static final String SYS_ROLE_DEPT = "sys_role_dept";
	private static final String SRD_DEPT_NO = "srd.dept_no";
	private static final String SRD = "srd";
	private static final String SYS_USER_ROLE = "sys_user_role";
	private static final String SUR = "sur";
	private static final String SUR_ROLE_ID = "sur.role_id";
	private static final String SRD_ROLE_ID = "srd.role_id";
	private static final String SUR_USER_ID = "sur.user_id";
	private static final String PERCENTAGE_SIGN = "%";
	private static final String SPLICING = "%s.%s";

	private static final String CREATED_BY = "created_by";
	private static final String CREATED_FROM = "created_from";

	@Override
	public Expression getSqlSegment(Expression where, String mappedStatementId) {
		long loginUserId = SecurityUtil.getLoginUserId();
		Set<SysRoleDataScopeEnum> dataScopes = SecurityUtil.getDataScopes();
		String deptNo = SecurityUtil.getDeptNo();
		// 固定范围按照范围大优先
		SysRoleDataScopeEnum dataScopeWithNonCustomization = getDataScopeWithNonCustomization(dataScopes);
		if (Objects.equals(SysRoleDataScopeEnum.ALL, dataScopeWithNonCustomization)) {
			// 查询全部数据 不拼接SQL
			return where;
		}
		// 别名识别
		DataScope dataScope = MappedStatementUtils.getMethodAnnotation(mappedStatementId, DataScope.class);
		String tableAlias = null;
		if (Objects.nonNull(dataScope)) {
			tableAlias = dataScope.tableAlias();
		}
		Expression rightExpression = null;
		if (Objects.equals(SysRoleDataScopeEnum.SELF, dataScopeWithNonCustomization)) {
			// 只查询自己的数据 拼接SQL create_at = userId
			rightExpression = new EqualsTo(
					getColumn(tableAlias, CREATED_BY),
					new LongValue(loginUserId)
			);
		} else if (Objects.equals(SysRoleDataScopeEnum.DEPARTMENT, dataScopeWithNonCustomization)) {
			// 查询本部门的数据 拼接SQL created_from = dept_no
			rightExpression = new EqualsTo(
					getColumn(tableAlias, CREATED_FROM),
					new StringValue(deptNo)
			);
		} else if (Objects.equals(SysRoleDataScopeEnum.DEPARTMENT_AND_SUBORDINATE, dataScopeWithNonCustomization)) {
			// 查询本部门及以下的数据 拼接SQL created_from in (dept_no, ...)
			rightExpression = new LikeExpression()
					.withLeftExpression(getColumn(tableAlias, CREATED_FROM))
					.withRightExpression(new StringValue(deptNo + PERCENTAGE_SIGN));
		}
		// 自定义数据范围
		if (dataScopes.contains(SysRoleDataScopeEnum.CUSTOMIZE)) {
			// 自定义数据范围 拼接SQL
			// Create SELECT body
			PlainSelect selectBody = getPlainSelect(loginUserId);
			if (Objects.nonNull(rightExpression)) {
				rightExpression = new OrExpression(rightExpression, new InExpression(
						getColumn(tableAlias, CREATED_FROM),
						new Parenthesis(selectBody))
				);
			} else {
				rightExpression = selectBody;
			}
		}
		return new AndExpression(where, new Parenthesis(rightExpression));
	}

	/**
	 * 获取列
	 *
	 * @param tableAlias 表别名
	 * @param column     柱
	 * @return {@link Column }
	 */
	private static Column getColumn(String tableAlias, String column) {
		return new Column(StrUtil.isBlank(tableAlias) ? column : String.format(SPLICING, tableAlias, column));
	}

	/**
	 * 根据用户ID获取部门编号的SQL组成
	 * SELECT srd.dept_no
	 * FROM sys_role_dept AS srd
	 * INNER JOIN sys_user_role AS sur ON srd.role_id = sur.role_id
	 * WHERE sur.user_id = ?
	 *
	 * @param userId 用户id
	 * @return {@link PlainSelect }
	 */
	private static PlainSelect getPlainSelect(Long userId) {
		PlainSelect selectBody = new PlainSelect();
		selectBody.setSelectItems(List.of(new SelectItem<>(new Column(SRD_DEPT_NO))));
		Table fromTable = new Table(SYS_ROLE_DEPT);
		fromTable.setAlias(new Alias(SRD));
		Table joinTable = new Table(SYS_USER_ROLE);
		joinTable.setAlias(new Alias(SUR));
		Join join = new Join();
		join.setInner(true);
		join.setRightItem(joinTable);
		join.setOnExpressions(List.of(new EqualsTo(new Column(SRD_ROLE_ID), new Column(SUR_ROLE_ID))));
		selectBody.setFromItem(fromTable);
		selectBody.setJoins(List.of(join));
		selectBody.setWhere(new EqualsTo(new Column(SUR_USER_ID), new LongValue(userId)));
		return selectBody;
	}

	/**
	 * 获取非自定义数据范围
	 *
	 * @param dataScopes 数据作用域
	 * @return int
	 */
	private static SysRoleDataScopeEnum getDataScopeWithNonCustomization(Set<SysRoleDataScopeEnum> dataScopes) {
		SysRoleDataScopeEnum dataScope = SysRoleDataScopeEnum.SELF;
		if (dataScopes.contains(SysRoleDataScopeEnum.DEPARTMENT)) {
			dataScope = SysRoleDataScopeEnum.DEPARTMENT;
		}
		if (dataScopes.contains(SysRoleDataScopeEnum.DEPARTMENT_AND_SUBORDINATE)) {
			dataScope = SysRoleDataScopeEnum.DEPARTMENT_AND_SUBORDINATE;
		}
		if (dataScopes.contains(SysRoleDataScopeEnum.ALL)) {
			dataScope = SysRoleDataScopeEnum.ALL;
		}
		return dataScope;
	}
}
